
import com.sun.jna.Pointer
import com.sun.jna.platform.win32.User32
import com.sun.jna.platform.win32.WinDef.HWND
import com.sun.jna.platform.win32.WinUser.GWL_STYLE
import com.sun.jna.platform.win32.WinUser.WS_CAPTION
import com.sun.jna.platform.win32.WinUser.WS_MAXIMIZEBOX
import com.sun.jna.platform.win32.WinUser.WS_THICKFRAME
import io.qt.Nullable
import io.qt.QtUtilities
import io.qt.core.QEvent
import io.qt.core.QList
import io.qt.core.QObject
import io.qt.core.QPoint
import io.qt.core.QRect
import io.qt.core.QVersionNumber
import io.qt.core.Qt
import io.qt.gui.QHoverEvent
import io.qt.gui.QMouseEvent
import io.qt.gui.QShowEvent
import io.qt.widgets.QMainWindow
import io.qt.widgets.QWidget


open class FrameLessMainWindow : QMainWindow() {

    //边距+可移动+可拉伸
    private var padding = 8
    private var moveEnable: Boolean = true
    private var resizeEnable: Boolean = true

    //标题栏控件
    private var titleBar = QWidget()

    //鼠标是否按下+按下坐标+按下时窗体区域
    private var mousePressed = false
    private var mousePoint = QPoint(0, 0)
    private var mouseRect = QRect(0, 0, 0, 0)

    //鼠标是否按下某个区域+按下区域的大小
    //依次为 左侧+右侧+上侧+下侧+左上侧+右上侧+左下侧+右下侧
    private val pressedArea = QList(8, false)
    private val pressedRect: QList<QRect> = QList(8, QRect(0, 0, 0, 0))

    //记录是否最小化
    private var isMin = false

    //存储窗体默认的属性
    private val flags = this.windowFlags()

    // defining signals in kotlin
    val titleDblClick: Signal0 = Signal0()
    val windowStateChange: Signal1<Boolean> = Signal1<Boolean>()

    // defining slots
    //设置边距+可拖动+可拉伸
    fun setPadding(padding: Int) {
        this.padding = padding
    }

    fun setMoveEnable(moveEnable: Boolean) {
        this.moveEnable = moveEnable
    }

    fun setResizeEnable(resizeEnable: Boolean) {
        this.resizeEnable = resizeEnable
    }

    //设置标题栏控件
    fun setTitleBar(titleBar: QWidget) {
        this.titleBar = titleBar
        this.titleBar.installEventFilter(this)
    }

    init {
        //设置背景透明 官方在5.3以后才彻底修复 WA_TranslucentBackground+FramelessWindowHint 并存不绘制的bug
        if (QtUtilities.qtjambiVersion().compareTo(QVersionNumber(5, 3, 0)) == 1) {
            this.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)

        }
        //设置无边框属性
        this.setWindowFlags(Qt.WindowType.FramelessWindowHint)
        //安装事件过滤器识别拖动
        this.installEventFilter(this)
        //设置属性产生win窗体效果,移动到边缘半屏或者最大化等
        //设置以后会产生标题栏,需要在下面拦截消息WM_NCCALCSIZE重新去掉
        val hwnd = HWND(Pointer(this.winId()))
        val style = User32.INSTANCE.GetWindowLong(hwnd, User32.GWL_STYLE)
        User32.INSTANCE.SetWindowLong(hwnd, GWL_STYLE, style or WS_MAXIMIZEBOX or WS_THICKFRAME or WS_CAPTION)
    }


    override fun showEvent(event: @Nullable QShowEvent?) {
        //解决有时候窗体重新显示的时候假死不刷新的bug
        setAttribute(Qt.WidgetAttribute.WA_Mapped)
        super.showEvent(event)
    }

    private fun doWindowStateChange(event: QEvent) {
        //非最大化才能移动和拖动大小
        if (windowState().testFlags(Qt.WindowState.WindowNoState)) {
            moveEnable = true
            resizeEnable = true
        } else {
            moveEnable = false
            resizeEnable = false
        }

        //发出最大化最小化等改变事件,以便界面上更改对应的信息比如右上角图标和文字
        this.windowStateChange.emit(!moveEnable)

        //解决mac系统上无边框最小化失效的bug
        if (System.getProperty("os.name").lowercase().contains("mac")) {


            if (windowState().value() and Qt.WindowState.WindowMinimized.value() != 0) {
                isMin = true

            } else {
                if (isMin) {
                    //设置无边框属性
                    this.setWindowFlags(Qt.WindowFlags(flags.value() or Qt.WindowType.FramelessWindowHint.value()))
                    this.isVisible = true
                    isMin = false
                }
            }
        }
    }

    private fun doResizeEvent(event: QEvent) {
        //非win系统的无边框拉伸,win系统上已经采用了nativeEvent来处理拉伸
        //为何不统一用计算的方式因为在win上用这个方式往左拉伸会发抖妹的
        if (System.getProperty("os.name").lowercase().contains("win")) {
            val type = event.type()
            if (type == QEvent.Type.Resize) {
                println("QEvent.Type.Resize")
                //重新计算八个描点的区域,描点区域的作用还有就是计算鼠标坐标是否在某一个区域内
                val width = this.width()
                val height = this.height()

                //左侧描点区域
                pressedRect[0] = QRect(0, padding, padding, height - padding * 2)
                //右侧描点区域
                pressedRect[1] = QRect(width - padding, padding, padding, height - padding * 2)
                //上侧描点区域
                pressedRect[2] = QRect(padding, 0, width - padding * 2, padding)
                //下侧描点区域
                pressedRect[3] = QRect(padding, height - padding, width - padding * 2, padding)

                //左上角描点区域
                pressedRect[4] = QRect(0, 0, padding, padding)
                //右上角描点区域
                pressedRect[5] = QRect(width - padding, 0, padding, padding)
                //左下角描点区域
                pressedRect[6] = QRect(0, height - padding, padding, padding)
                //右下角描点区域
                pressedRect[7] = QRect(width - padding, height - padding, padding, padding)
            } else if (mousePressed&&type == QEvent.Type.HoverMove) {
                println("QEvent.Type.HoverMove")
                //设置对应鼠标形状,这个必须放在这里而不是下面,因为可以在鼠标没有按下的时候识别
                val hoverEvent = event as QHoverEvent
                val point = hoverEvent.pos()
                mouseRect = this.geometry
                if (resizeEnable) {
                    if (pressedRect.at(0).contains(point)) {
                        this.setCursor(Qt.CursorShape.SizeHorCursor)
                    } else if (pressedRect.at(1).contains(point)) {
                        this.setCursor(Qt.CursorShape.SizeHorCursor)
                    } else if (pressedRect.at(2).contains(point)) {
                        this.setCursor(Qt.CursorShape.SizeVerCursor)
                    } else if (pressedRect.at(3).contains(point)) {
                        this.setCursor(Qt.CursorShape.SizeVerCursor)
                    } else if (pressedRect.at(4).contains(point)) {
                        this.setCursor(Qt.CursorShape.SizeFDiagCursor)
                    } else if (pressedRect.at(5).contains(point)) {
                        this.setCursor(Qt.CursorShape.SizeBDiagCursor)
                    } else if (pressedRect.at(6).contains(point)) {
                        this.setCursor(Qt.CursorShape.SizeBDiagCursor)
                    } else if (pressedRect.at(7).contains(point)) {
                        this.setCursor(Qt.CursorShape.SizeFDiagCursor)
                    } else {
                        this.setCursor(Qt.CursorShape.ArrowCursor)
                    }
                }

                //根据当前鼠标位置,计算XY轴移动了多少
                val offsetX = point.x() - mousePoint.x()
                val offsetY = point.y() - mousePoint.y()

                //根据按下处的位置判断是否是移动控件还是拉伸控件
                if (moveEnable && mousePressed) {
                    this.move(this.x() + offsetX, this.y() + offsetY)
                }

                if (resizeEnable) {
                    val rectX = mouseRect.x()
                    val rectY = mouseRect.y()
                    val rectW = mouseRect.width()
                    val rectH = mouseRect.height()

                    if (pressedArea.at(0) != null) {
                        val resizeW = this.width() - offsetX
                        if (this.minimumWidth() <= resizeW) {
                            this.setGeometry(this.x() + offsetX, rectY, resizeW, rectH)
                        }
                    } else if (pressedArea.at(1)) {
                        this.setGeometry(rectX, rectY, rectW + offsetX, rectH)
                    } else if (pressedArea.at(2)) {
                        val resizeH = this.height() - offsetY
                        if (this.minimumHeight() <= resizeH) {
                            this.setGeometry(rectX, this.y() + offsetY, rectW, resizeH)
                        }
                    } else if (pressedArea.at(3)) {
                        this.setGeometry(rectX, rectY, rectW, rectH + offsetY)
                    } else if (pressedArea.at(4)) {
                        val resizeW = this.width() - offsetX
                        val resizeH = this.height() - offsetY
                        if (this.minimumWidth() <= resizeW) {
                            this.setGeometry(this.x() + offsetX, this.y(), resizeW, resizeH)
                        }
                        if (this.minimumHeight() <= resizeH) {
                            this.setGeometry(this.x(), this.y() + offsetY, resizeW, resizeH)
                        }
                    } else if (pressedArea.at(5)) {
                        val resizeW = rectW + offsetX
                        val resizeH = this.height() - offsetY
                        if (this.minimumHeight() <= resizeH) {
                            this.setGeometry(this.x(), this.y() + offsetY, resizeW, resizeH)
                        }
                    } else if (pressedArea.at(6)) {
                        val resizeW = this.width() - offsetX
                        val resizeH = rectH + offsetY
                        if (this.minimumWidth() <= resizeW) {
                            this.setGeometry(this.x() + offsetX, this.y(), resizeW, resizeH)
                        }
                        if (this.minimumHeight() <= resizeH) {
                            this.setGeometry(this.x(), this.y(), resizeW, resizeH)
                        }
                    } else if (pressedArea.at(7)) {
                        val resizeW = rectW + offsetX
                        val resizeH = rectH + offsetY
                        this.setGeometry(this.x(), this.y(), resizeW, resizeH)
                    }
                }
            } else if (type == QEvent.Type.MouseButtonPress) {
                println("QEvent.Type.MouseButtonPress")
                //记住鼠标按下的坐标+窗体区域
                val mouseEvent = event as QMouseEvent
                mousePoint = mouseEvent.pos()
                mouseRect = this.geometry()

                //判断按下的手柄的区域位置
                if (pressedRect.at(0).contains(mousePoint)) {
                    pressedArea[0] = true
                } else if (pressedRect.at(1).contains(mousePoint)) {
                    pressedArea[1] = true
                } else if (pressedRect.at(2).contains(mousePoint)) {
                    pressedArea[2] = true
                } else if (pressedRect.at(3).contains(mousePoint)) {
                    pressedArea[3] = true
                } else if (pressedRect.at(4).contains(mousePoint)) {
                    pressedArea[4] = true
                } else if (pressedRect.at(5).contains(mousePoint)) {
                    pressedArea[5] = true
                } else if (pressedRect.at(6).contains(mousePoint)) {
                    pressedArea[6] = true
                } else if (pressedRect.at(7).contains(mousePoint)) {
                    pressedArea[7] = true
                } else {
                    mousePressed = true
                }
            } else if (type == QEvent.Type.MouseMove) {
                //改成用HoverMove识别
                println("QEvent.Type.MouseMove")
            } else if (type == QEvent.Type.MouseButtonRelease) {
                println("QEvent.Type.MouseButtonRelease")
                //恢复所有
                this.setCursor(Qt.CursorShape.ArrowCursor)
                mousePressed = false
                pressedArea.fill(false)
            }
        }
    }

    override fun eventFilter(watched: @Nullable QObject?, event: @Nullable QEvent?): Boolean {
        val type = event?.type()
        if (watched == this) {
            if (type == QEvent.Type.WindowStateChange) {
                doWindowStateChange(event)
            } else {
                event?.let { doResizeEvent(it) }
            }
        } else if (watched == titleBar) {
            //双击标题栏发出双击信号给主界面
            //下面的 *result = HTCAPTION; 标志位也会自动识别双击标题栏
            if (System.getProperty("os.name").lowercase().contains("win")) {
                if (type == QEvent.Type.MouseButtonDblClick) {
                    this.titleDblClick.emit()
                } else if (type == QEvent.Type.NonClientAreaMouseButtonDblClick) {
                    this.titleDblClick.emit()
                }
            }
        }
        return super.eventFilter(watched, event)
    }


}